package com.wzh.glsc.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.wzh.common.to.es.SkuEsModel;
import com.wzh.common.utils.R;
import com.wzh.glsc.search.config.MallElasticSearchConfig;
import com.wzh.glsc.search.constant.EsConstant;
import com.wzh.glsc.search.feign.ProductFeignService;
import com.wzh.glsc.search.service.MallSearchService;
import com.wzh.glsc.search.vo.AttrsResponseVo;
import com.wzh.glsc.search.vo.SearchParam;
import com.wzh.glsc.search.vo.SearchResult;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author xiaoming
 * @PackageName:com.wzh.glsc.search.service.Impl
 * @ClassName:@{NAME}
 * @Description:
 * @date:@{DATE}@{TIME}
 */
@Service("MallSearchService")
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private ProductFeignService productFeignService;

    @Override
    public SearchResult searchEsBySearchParam(SearchParam searchParam) {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(EsConstant.PRODUCT_INDEX);
        searchRequest.source(getSourceBuilderByParam(searchParam));
        SearchResponse search = null;
        try {
            search = restHighLevelClient.search(searchRequest, MallElasticSearchConfig.COMMON_OPTIONS);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(search);
        return getSearchResultBySearchResponse(search, searchParam);
    }

    //封装条件
    private SearchSourceBuilder getSourceBuilderByParam(SearchParam searchParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //查询条件
        // "query": {"bool":{}}
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //bool":{"must"{"match": { "skuTitle": #keyword}}}
        if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("skuTitle", searchParam.getKeyword()));
        }
        //过滤条件
        //bool":{"filter"{"term": { "catalogId": #catalog3Id}}}
        if (searchParam.getCatalog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
        }
        //bool":{"filter"{"term": { "hasStock": true}}}
        if (searchParam.getHasStock() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() > 0 ? true : false));
        }
        if (StringUtils.isNotEmpty(searchParam.getSkuPrice())) {
            String[] str = searchParam.getSkuPrice().split("_");
            if (str.length == 2) {//bool":{"filter"{"range": { "skuPrice": {"gte":"#str[0]"},"lte":"#str[1]"}}}
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(str[0]).lte(str[1]));
            } else {
                if (searchParam.getSkuPrice().endsWith("_")) {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").gte(searchParam.getSkuPrice()));
                } else {
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("skuPrice").lte(searchParam.getSkuPrice()));
                }
            }
        }
        if (searchParam.getAttrs() != null && searchParam.getAttrs().size() > 0) {
            Iterator<String> iterator = searchParam.getAttrs().stream().iterator();
            while (iterator.hasNext()) {
//               bool":{"filter"{ {"nested": {"path": "attrs",
//                            "query": {"bool": {"must": [{"terms": {
//                                          "attrs.attrValue": ["#iterator.next()"]
//                }}]}}}}}
                boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs",
                        QueryBuilders.boolQuery().must(
                                QueryBuilders.termQuery("attrs.attrId",
                                        iterator.next().split("_")[0])),
                        ScoreMode.None));
            }
        }
        searchSourceBuilder.query(boolQueryBuilder);

        //聚合:子聚合"aggs": {"b_aggs":{"terms": {"field":"bId"}},"img_aggs":{"terms": {"field":"img"}} }
        TermsAggregationBuilder brandsAggs = AggregationBuilders.terms("brands_aggs").field("brandId");
        brandsAggs.subAggregation(AggregationBuilders.terms("brands_img_agg").field("brandImg"));
        brandsAggs.subAggregation(AggregationBuilders.terms("brands_name_agg").field("brandName"));

        //嵌入式类型聚合
        TermsAggregationBuilder attrTermsAgg = AggregationBuilders.terms("attrId_aggs").field("attrs.attrId");
        attrTermsAgg.subAggregation(AggregationBuilders.terms("attrName_aggs").field("attrs.attrName"));
        attrTermsAgg.subAggregation(AggregationBuilders.terms("attrValue_aggs").field("attrs.attrValue"));
        NestedAggregationBuilder attrNested = AggregationBuilders.nested("attrs_aggs", "attrs");
        attrNested.subAggregation(attrTermsAgg);

        //三级分类
        TermsAggregationBuilder categoryAggs = AggregationBuilders.terms("categorys_aggs").field("catalogId");
        categoryAggs.subAggregation(AggregationBuilders.terms("name_agg").field("catalogName"));
        searchSourceBuilder.aggregation(brandsAggs);
        searchSourceBuilder.aggregation(attrNested);
        searchSourceBuilder.aggregation(categoryAggs);

        //高亮
        searchSourceBuilder.highlighter(new HighlightBuilder().field("skuTitle").preTags("<b style='color:red'>").postTags("</b>"));
        searchSourceBuilder.from((searchParam.getPageNum() != null ? searchParam.getPageNum() : 1 - 1) * EsConstant.PRODUCT_PAGESIZE);
        //saleCount销量、skuPrice价格、hotScore热度
        if (searchParam.getSort() != null) {
            String[] s = searchParam.getSort().split("_");
            searchSourceBuilder.sort(s[0], s[1].equals(SortOrder.ASC) ? SortOrder.ASC : SortOrder.DESC);
        }
        return searchSourceBuilder;
    }

    private SearchResult getSearchResultBySearchResponse(SearchResponse search, SearchParam searchParam) {
        SearchHit[] hits = search.getHits().getHits();
        SearchResult searchResult = new SearchResult();
        List<SkuEsModel> skuEsModels = Arrays.asList(hits).stream().map(hit -> {
            SkuEsModel skuEsModel = JSON.parseObject(hit.getSourceAsString(), SkuEsModel.class);
            if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
                skuEsModel.setSkuTitle(hit.getHighlightFields().get("skuTitle").getFragments()[0].string());
            }
            return skuEsModel;
        }).collect(Collectors.toList());

        searchResult.setProducts(skuEsModels); //所有商品信息
        searchResult.setPageNum(searchParam.getPageNum());//当前页码
        searchResult.setTotal(search.getHits().getTotalHits().value);//总记录数
        Long totalPage = search.getHits().getTotalHits().value / EsConstant.PRODUCT_PAGESIZE;
        searchResult.setTotalPages(search.getHits().getTotalHits().value % EsConstant.PRODUCT_PAGESIZE == 0 ? totalPage.intValue() : totalPage.intValue() + 1);//总页码
        //所有涉及到的品牌
        Terms brandsAggs = search.getAggregations().get("brands_aggs");
        List<SearchResult.BrandVo> brandVoCollect = brandsAggs.getBuckets().stream().map(bucket -> {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
            brandVo.setBrandImg(((Terms) (bucket.getAggregations().get("brands_img_agg"))).getBuckets().get(0).getKey().toString());
            brandVo.setBrandName(((Terms) (bucket.getAggregations().get("brands_name_agg"))).getBuckets().get(0).getKey().toString());
            return brandVo;
        }).collect(Collectors.toList());
        searchResult.setBrands(brandVoCollect);
        //所有涉及到的规格
        ParsedNested attrParsed = search.getAggregations().get("attrs_aggs");
        ParsedLongTerms attrAggs = attrParsed.getAggregations().get("attrId_aggs");
        List<SearchResult.AttrVo> collect = attrAggs.getBuckets().stream().map(bucket -> {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
            attrVo.setAttrName(((ParsedStringTerms) bucket.getAggregations().get("attrName_aggs")).getBuckets().get(0).getKeyAsString());
            ParsedStringTerms attrValueTerms = bucket.getAggregations().get("attrValue_aggs");
            List<String> attrValueCollect = attrValueTerms.getBuckets().stream().map(str -> {
                return ((Terms.Bucket) str).getKeyAsString();
            }).collect(Collectors.toList());
            attrVo.setAttrValue(attrValueCollect);
            return attrVo;
        }).collect(Collectors.toList());
        searchResult.setAttrs(collect);
        //所有涉及到的类型
        Terms categoryVoAggs = search.getAggregations().get("categorys_aggs");
        List<SearchResult.CategoryVo> categoryVoAggsCollect = categoryVoAggs.getBuckets().stream().map(bucket -> {
            SearchResult.CategoryVo categoryVo = new SearchResult.CategoryVo();
            categoryVo.setCategoryId(bucket.getKeyAsNumber().longValue());
            categoryVo.setCategoryName(((Terms) (bucket.getAggregations().get("name_agg"))).getBuckets().get(0).getKey().toString());
            return categoryVo;
        }).collect(Collectors.toList());
        searchResult.setCategorys(categoryVoAggsCollect);
        //页码
        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 0; i < totalPage; i++) {
            pageNavs.add(i);
        }
        searchResult.setPageNavs(pageNavs);
        //面包屑
        if (searchParam.getAttrs() != null) {
            List<SearchResult.NavVo> navVos = searchParam.getAttrs().stream().map(attr -> {
                String[] attrStr = attr.split("_");
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                R r = productFeignService.info(Long.parseLong(attrStr[0]));
                if (r.getCode() == 0) {
                    AttrsResponseVo attrVo = r.getData("attr", new TypeReference<AttrsResponseVo>() {
                    });
                    navVo.setNavName(attrVo.getAttrName());
                } else {
                    navVo.setNavName(attrStr[0]);
                }
                //去年面包屑后
                String encode=null;
                try {
                    encode = URLEncoder.encode(attr, "UTF-8");
                    encode=encode.replace("+", "%20");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                String replace = searchParam.get_queryString().replace("&attrs=" + encode, "");
                navVo.setLink(EsConstant.SEARCH_URL + replace);
                return navVo;
            }).collect(Collectors.toList());
            searchResult.setNavs(navVos);
        }
        return searchResult;
    }
}
